package com.running.base.run.util;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Map;

public class HttpsClientUtils {

    private static final Logger logger = LoggerFactory.getLogger(HttpsClientUtils.class);

    private static volatile CloseableHttpClient httpClient = null;

    private static final String defaultCharset = Constants.CHAR_SET;

    //设置连接超时时间
    private static final Integer CONNECTION_TIMEOUT = 10 * 1000;
    private static final Integer SO_TIMEOUT = 15 * 1000;
    private static final Integer CONN_MANAGER_TIMEOUT = 3 * 1000;

    public static CloseableHttpClient getHttpClient() throws Exception {
        if (httpClient == null) {
            synchronized (HttpsClientUtils.class) {
                if (httpClient == null) {
                    init();
                }
            }
        }
        return httpClient;
    }

    private static void init() throws Exception {
        try {
            httpClient = sslClient();
        } catch (Exception e) {
            logger.error("init() httpsclient error", e);
        }
    }

    private static String getQueryUrl(String url, Map<String, String> param) {
        StringBuilder formatUrl = new StringBuilder();
        if (!CollectionUtils.isEmpty(param)) {
            for (Map.Entry<String, String> entry : param.entrySet()) {
                formatUrl.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }

        String res = formatUrl.toString();
        return url + "?" + res.substring(0, res.length() - 1);
    }

    public static String sendGet(String url, Map<String, String> param) throws Exception {
        String formatUrl = null;
        try {
            formatUrl = getQueryUrl(url, param);
            HttpGet httpGet = new HttpGet(formatUrl);
            HttpResponse response = null;
            response = HttpsClientUtils.getHttpClient().execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
                throw new RuntimeException("HttpsClientUtils error status code :" + statusCode + ",aliUrl=" + formatUrl);
            }
            String message = EntityUtils.toString(response.getEntity(), defaultCharset);
            logger.info("Method : HttpsClients | sendGet res===" + message + ",aliUrl===" + formatUrl);
            return message;
        } catch (IOException e) {
            logger.error("Method : HttpsClientUtils | sendGet error ! aliUrl = " + formatUrl, e);
        }

        return null;

    }

    public static String sendPost(String url, String jsonBody) throws Exception {
        try {
            HttpPost httpPost = new HttpPost(url);
            StringEntity stringEntity = new StringEntity(jsonBody, Constants.CHAR_SET);
            httpPost.setEntity(stringEntity);
            httpPost.setHeader("Content-Type", "application/json");
            HttpResponse resp = HttpsClientUtils.getHttpClient().execute(httpPost);
            int statusCode = resp.getStatusLine().getStatusCode();
            if (HttpStatus.SC_OK != statusCode) {
                throw new RuntimeException("HttpsClientUtils error status code :" + statusCode + ",aliUrl=" + url);
            }

            String message = EntityUtils.toString(resp.getEntity(), defaultCharset);
            logger.info("Method : HttpsClients | sendPost res===" + message + ",aliUrl===" + url);
            return message;
        } catch (IOException e) {
            logger.error("Method : HttpsClientUtils | sendPost error，aliUrl = " + url, e);
        }
        return null;
    }

    private static CloseableHttpClient sslClient() throws Exception {
        try {
            // 在调用SSL之前需要重写验证方法，取消检测SSL
            X509TrustManager trustManager = new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                @Override
                public void checkClientTrusted(X509Certificate[] xcs, String str) {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] xcs, String str) {
                }
            };
            SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
            ctx.init(null, new TrustManager[]{trustManager}, null);
            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
            // 创建Registry
            RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT)
                    .setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
                    .setConnectTimeout(CONNECTION_TIMEOUT).setSocketTimeout(SO_TIMEOUT).setStaleConnectionCheckEnabled(false).setConnectionRequestTimeout(CONN_MANAGER_TIMEOUT)
                    .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)).build();
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", socketFactory).build();
            // 创建ConnectionManager，添加Connection配置信息
            PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            connectionManager.setMaxTotal(800);
            connectionManager.setDefaultMaxPerRoute(400);

            SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();

            CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultRequestConfig(requestConfig).setDefaultSocketConfig(socketConfig).build();
            return closeableHttpClient;
        } catch (KeyManagementException ex) {
            logger.error("HttpClientUtils sslClient KeyManagementException:->", ex);
            throw new KeyManagementException();
        } catch (NoSuchAlgorithmException ex) {
            logger.error("HttpClientUtils sslClient NoSuchAlgorithmException:->", ex);
            throw new NoSuchAlgorithmException();
        } catch (Exception e) {
            logger.error("HttpClientUtils sslClient Exception:->", e);
            throw new Exception();
        }
    }

    /**
     * 简版忽略ssl验证-获取httpclient
     * @return
     */
    public static CloseableHttpClient createIgnoreSSLHttpClient () {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

